﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;

namespace JOATServer.SocietyFramework.Core
{
    /// <summary>
    /// The atom class of the Society Framework. Extend this to create a new automaton.
    /// Author: Micu Andrei
    /// </summary>
    public abstract class BaseAutomaton
    {
	    protected internal MessageList messageList;				      //holds the messages for the transitions
        protected internal Message currentMessage;                    //holds the last message that was removed from the list
        protected internal Dictionary<string, State> states;          //holds the code states in the automaton
        protected internal String currentStateName;                   //holds the name of the state in which the automaton currently is

        public BaseAutomaton()
        {
            messageList = new MessageList();
            states = new Dictionary<string, State>();
        }

        protected Thread automatonThread; 		        //the thread that runs the automaton
        protected bool running = false;				    //then the automaton thread is executed until running is false
        public delegate void Callback();
        protected Callback callback;				    //callback executed when the thread finishes

        /// <summary>
        /// The method is a continuous loop with the following actions:<br/><br/>
        /// - executes the code in the current state<br/>
        /// - executes the code from the current state's transition<br/>
        /// - if the state name returned by the transition code is not null, it becomes the current state<br/>
        /// - if the state name returned by the transition code is null, the transition code is called again<br />
        /// It terminates when the automaton's running flag is set to false and the loop ends.
        /// </summary>
        public virtual void Run()
        {
            BaseAutomaton.State currentState;
            running = true;
            while (running)
            {
                currentState = states[currentStateName];
                currentState.StateCode();								//executes the code in the state
                while ((currentStateName = currentState.transition.TransitionCode()) == null) ;
            }
        }

        /// <summary>
        /// Method that creates and starts the automaton thread.<br/>
        /// In case the thread is created and alive it will attempt to stop it and start a new one 
        /// (in this case the method returns immediately after it attempts to stop the automaton).
        /// </summary>
        public virtual void Start()
        {
		    if(automatonThread!=null && automatonThread.IsAlive){
			    this.StopSafely(() => 
                {
                    automatonThread = new Thread(() =>
                    {
                        Run();
			            if(callback!=null)
                        {
                            callback();		//execute callback method after stopping the while loop
                        }
                    });
                    automatonThread.Name = "AutomatonThread_" + this.GetType().Name;
					automatonThread.Start();
			    });
		    } else {
                automatonThread = new Thread(() =>
                {
                    Run();
                    if (callback != null)
                    {
                        callback();		//execute callback method after stopping the while loop
                    }
                });
                automatonThread.Name = "AutomatonThread_" + this.GetType().Name;
                automatonThread.Start();
		    }
	    }

        /// <summary>
        /// Method used to safely end the thread.
        /// </summary>
        public virtual void StopSafely()
        {
            running = false;
            messageList.CancelRemoving();
	    }

        /// <summary>
        /// Method used to safely end the thread. A callback method is executed after successfully stopped.
        /// </summary>
        /// <param name="callback">Delegate method executed after successfully stopping the automaton.</param>
        public virtual void StopSafely(Callback callback)
        {
		    this.callback = callback;
            running = false;
            messageList.CancelRemoving();
	    }

        /// <summary>
        /// Adds a Message to the tail of the MessageList.
        /// </summary>
        /// <param name="message"></param>
        public virtual void AddMessage(Message message)
        {
		    messageList.Add(message);
	    }

        /// <summary>
        /// Gets the next message from the messageList and assigns it to the currentMessage member.
        /// </summary>
        protected internal virtual void GetNextMessage()
        {
            currentMessage = messageList.Remove();
        }

        /// <summary>
        /// Gets the next message from the messageList and assigns it to the currentMessage member. 
        /// If no message is available after the timer expires then the currentMessage will be a NullMessage.
        /// </summary>
        /// <param name="timer">the amount of time to wait until the function returns</param>
        protected internal virtual void GetNextMessage(int timer)
        {
            currentMessage = messageList.Remove(timer);
        }

        /// <summary>
        /// A State is represented by:
        /// <p>1. StateCode() -> a method which holds the code executed in this state</p>
        /// <p>2. transition -> a transition that is evaluated in order to go to the next state (this can be a Transition when there is
        /// only one simple transition or a TransitionGroup when there are multiple possibilities)</p>
        /// </summary>
        protected internal abstract class State
        {
            protected BaseAutomaton Owner { get; set; }
            public Transition transition;

            public State(BaseAutomaton owner)
            {
                this.Owner = owner;
            }

            /// <summary>
            /// Method which holds the code executed in the containing State.
            /// </summary>
            public abstract void StateCode();
        }

        /// <summary>
        /// A NullState is a convenient class which represents a state that does nothing.
        /// Use this class to avoid creating redundant state classes with no code inside.
        /// </summary>
        protected internal class NullState : State
        {
            public NullState(BaseAutomaton owner)
                : base(owner)
            {
                this.Owner = owner;
            }

            public override void StateCode() { }
        }

        /// <summary>
        /// A Transition is represented by TransitionCode() function ->
        /// method that evaluates the message and returns the name of the next state.
        /// </summary>
        protected internal abstract class Transition
        {
            protected BaseAutomaton Owner { get; set; }

            public Transition(BaseAutomaton owner)
            {
                this.Owner = owner;
            }

            /// <summary>
            /// Method that performs the transition to the next state.
            /// </summary>
            /// <returns>the name of the next state (if the conditions in the code are satisfied) 
            /// or null (if the conditions are not satisfied)</returns>
		    public abstract string TransitionCode();
	    }

        /// <summary>
        /// A SimpleTransition is a convenient class for states that have only one transition t a single state.
        /// Use this class to avoid creating redundant transition classes.
        /// </summary>
        protected internal class SimpleTransition : Transition
        {
		    protected readonly string stateName;

            public SimpleTransition(BaseAutomaton owner, string stateName)
                : base(owner)
            {
			    this.stateName = stateName;
		    }
		
		    public override string TransitionCode(){
			    return stateName;
		    }
	    }

        /// <summary>
        /// A TransitionGroup is a convenient class used to manage indeterminism in automatons.
        /// If more than one possibilities exist you must override the TransitionCode() method
        /// to return a single state name depending on the transitions in transitionsList.
        /// </summary>
        protected internal class TransitionGroup : Transition
        {
		    public readonly Dictionary<string, Transition> transitions = new Dictionary<string, Transition>();

            public TransitionGroup(BaseAutomaton owner) : base(owner){ }
		
		    /// <summary>
		    /// By default, this TransitionCode() method returns the first non-null state name returned by a transition 
            /// in the transitions map. If no transition returns a string, then null is returned.
		    /// </summary>
            /// <returns>First state name returned by a transition in the map or null if no string is returned by any.</returns>
		    public override String TransitionCode(){
			    String name;
			    foreach(Transition transition in transitions.Values){
				    name=transition.TransitionCode();
				    if(name!=null) return name;
			    }
			    return null;
		    }
	    }

        /// <summary>
        /// Method in which the Automaton state is saved and returned as String.
        /// </summary>
        /// <returns>String representation of the Automaton state</returns>
        public abstract String SerializeToString();

        /// <summary>
        /// Method in which the Automaton state is saved and written in a stream.
        /// </summary>
        /// <param name="outputStream">stream in which the automaton state is saved</param>
        /// <returns>true if the operation succeeded, false otherwise</returns>
        public abstract bool SerializeToStream(Stream outputStream);

        /// <summary>
        /// Method which changes the Automaton state with the one from the representation.
        /// </summary>
        /// <param name="serialization">the String representation of the Automaton state to be loaded</param>
        /// <returns>true if the operation succeeded, false otherwise</returns>
        public abstract bool DeserializeFromString(String serialization);

        /// <summary>
        /// Method which changes the Automaton state with the one from the representation.
        /// </summary>
        /// <param name="inputStream">a stream from which the Automaton is restored</param>
        /// <returns>true if the operation succeeded, false otherwise</returns>
        public abstract bool DeserializeFromStream(Stream inputStream);
    }
}
